// Socket.cpp

#include "stdafx.h"
#include <stdlib.h>
#include "Socket.h"

#pragma warning(push)
#pragma warning(disable:4996)

int WinSock::s_iInitCnt=0;

bool WinSock::Init()
{
	if(s_iInitCnt<0){return false;};
	if(s_iInitCnt>=1){s_iInitCnt++;return true;};
	WSADATA xData;
	if(::WSAStartup(MAKEWORD(2,1),&xData)==0)
	{
		s_iInitCnt=1;
		return true;
	}
	else
	{
		s_iInitCnt=-1;
		return false;
	};
};

void WinSock::Shut()
{
	if(s_iInitCnt<0){return;};
	if(s_iInitCnt>1){s_iInitCnt--;return;};
	::WSACleanup();
	s_iInitCnt=0;
};

int WinSock::GetLastError()
{
	return ::WSAGetLastError();
};

void WinSock::ErrorCodeToText(int p_iError,char* p_sDest,int p_iDestSize)
{
	if(FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_FROM_SYSTEM,NULL,
		p_iError,0,p_sDest,p_iDestSize,NULL)==0)
	{
		if(p_iDestSize>=8)
		{
			strcpy(p_sDest,"unknown");
		}
		else
		{
			*p_sDest='\0';
		};
	};
};

IPAddr::IPAddr()
{
	sin_family=AF_INET;
	sin_port=0;
	sin_addr.S_un.S_addr=0;
	int i;
	for(i=0;i<8;i++)
	{
		sin_zero[i]=0;
	};
};

IPAddr::IPAddr(const IPAddr& p_xA)
{
	sin_family=p_xA.sin_family;
	int i;
	for(i=0;i<8;i++)
	{
		sin_zero[i]=0;
	};
	sin_port=p_xA.sin_port;
	sin_addr=p_xA.sin_addr;
};

IPAddr& IPAddr::operator=(const IPAddr& p_xA)
{
	sin_family=p_xA.sin_family;
	int i;
	for(i=0;i<8;i++)
	{
		sin_zero[i]=0;
	};
	sin_port=p_xA.sin_port;
	sin_addr=p_xA.sin_addr;
	return *this;
};

bool IPAddr::operator==(const IPAddr& p_xA) const
{
	return (sin_port==p_xA.sin_port)&&(sin_addr.S_un.S_addr==p_xA.sin_addr.S_un.S_addr);
};

bool IPAddr::operator!=(const IPAddr& p_xA) const
{
	return (sin_port!=p_xA.sin_port)||(sin_addr.S_un.S_addr!=p_xA.sin_addr.S_un.S_addr);
};

void IPAddr::SetPort(int p_iPort)
{
	sin_port=htons((short)p_iPort);
};

int IPAddr::GetPort() const
{
	return ntohs(sin_port);
};

void IPAddr::ToText(char* po_sDest,int p_iSize)
{
	sprintf_s(po_sDest,p_iSize,"%d.%d.%d.%d:%d",
		sin_addr.S_un.S_un_b.s_b1,sin_addr.S_un.S_un_b.s_b2,
		sin_addr.S_un.S_un_b.s_b3,sin_addr.S_un.S_un_b.s_b4,
		GetPort());
};

AStr IPAddr::ToStr() const
{
	return AStr::Create("%d.%d.%d.%d:%d",
		sin_addr.S_un.S_un_b.s_b1,sin_addr.S_un.S_un_b.s_b2,
		sin_addr.S_un.S_un_b.s_b3,sin_addr.S_un.S_un_b.s_b4,
		GetPort());
};

bool IPAddr::Set(const char* p_sHost,int p_iPort)
{
	bool bRet=false;
	hostent* pHE;
	sin_addr.S_un.S_un_b.s_b1=127;
	sin_addr.S_un.S_un_b.s_b2=0;
	sin_addr.S_un.S_un_b.s_b3=0;
	sin_addr.S_un.S_un_b.s_b4=1;
	int iPort=p_iPort;
	char sHN[128];
	int iL=strlen(p_sHost);
	if(iL>127){iL=127;};
	strncpy(sHN,p_sHost,iL);
	sHN[iL]='\0';
	char* pcColon=strchr(sHN,':');
	if(pcColon)
	{
		*pcColon='\0';
		if(iPort==-1)
		{
			iPort=atoi(pcColon+1);
		};
	};
	sin_port=htons((short)iPort);
	int iAddr=inet_addr(sHN);
	if(iAddr!=-1)
	{
		sin_addr.S_un.S_addr=iAddr;
		bRet=true;
	}
	else
	{
		pHE=gethostbyname(sHN);
		if(pHE)
		{
			if(pHE->h_length==4)
			{
				bRet=true;
				sin_addr=*((in_addr*)pHE->h_addr_list[0]);
			};
		};
	};
	return  bRet;
};

IPAddr::IPAddr(const char* p_sHost,int p_iPort)
{
	sin_family=AF_INET;
	sin_port=0;
	sin_addr.S_un.S_addr=0;
	int i;
	for(i=0;i<8;i++)
	{
		sin_zero[i]=0;
	};
	Set(p_sHost,p_iPort);
};

IPAddr::IPAddr(const char* p_sHostAndPort)
{
	sin_family=AF_INET;
	sin_port=0;
	sin_addr.S_un.S_addr=0;
	int i;
	for(i=0;i<8;i++)
	{
		sin_zero[i]=0;
	};
	Set(p_sHostAndPort,-1);
};

IPAddr& IPAddr::operator=(const char* p_sHostAndPort)
{
	Set(p_sHostAndPort);
	return *this;
};

IPAddr IPAddr::GetLocalDefault()
{
	hostent* pHE;
	pHE=gethostbyname("localhost");
	if(!pHE)
	{
		return IPAddr();
	};
	char sRealHostName[256]="localhost";
	strncpy(sRealHostName,pHE->h_name,255);
	sRealHostName[255]='\0';
	return IPAddr(sRealHostName);
};


TcpSocket::TcpSocket()
{
	m_iSock=INVALID_SOCKET;
	m_hCloseEvt=NULL;
	m_iLastError=0;
};

TcpSocket::~TcpSocket()
{
	Close();
};

bool TcpSocket::Connect(const IPAddr& p_xAddr,int p_iTimeoutMS)
{
	Close();
	m_iSock=::WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,0,WSA_FLAG_OVERLAPPED);
	if(m_iSock==INVALID_SOCKET)
	{
		m_iLastError=WinSock::GetLastError();
		return false;
	};
	if(p_iTimeoutMS==-1)
	{
		if(connect(m_iSock,(struct sockaddr*)&p_xAddr,sizeof(p_xAddr))<0)
		{
			Close();
			m_iLastError=WinSock::GetLastError();
			return false;
		};
		m_hCloseEvt=::WSACreateEvent();
		::WSAEventSelect(m_iSock,m_hCloseEvt,FD_CLOSE);
	}
	else
	{
		m_hCloseEvt=::WSACreateEvent();
		::WSAEventSelect(m_iSock,m_hCloseEvt,FD_CLOSE);
		if(connect(m_iSock,(struct sockaddr*)&p_xAddr,sizeof(p_xAddr))<0)
		{
			int iErr=WinSock::GetLastError();
			if(iErr!=WSAEWOULDBLOCK)
			{
				Close();
				m_iLastError=iErr;
				return false;
			};
		};
		if(p_iTimeoutMS<1000)
		{
			::Sleep(p_iTimeoutMS);
			p_iTimeoutMS=10;
		};
		fd_set xFS;
		xFS.fd_count=1;
		xFS.fd_array[0]=m_iSock;
		TIMEVAL xTV;
		xTV.tv_sec=p_iTimeoutMS/1000;
		xTV.tv_usec=p_iTimeoutMS%1000;
		if(select(0,NULL,&xFS,NULL,&xTV)!=1)
		{
			Close();
			m_iLastError=WinSock::GetLastError();
			return false;
		};
	};
	m_iLastError=WinSock::GetLastError();
	return true;
};

void TcpSocket::Close()
{
	if(m_iSock!=INVALID_SOCKET)
	{
		shutdown(m_iSock,SD_BOTH);
		closesocket(m_iSock);
		m_iSock=INVALID_SOCKET;

		if(m_hCloseEvt!=WSA_INVALID_EVENT)
		{
			::WSACloseEvent(m_hCloseEvt);
			m_hCloseEvt=WSA_INVALID_EVENT;
		};
	};
};

int TcpSocket::Send(const void* p_pData,int p_iSize)
{
	if(m_iSock==INVALID_SOCKET){return -1;};
	int iSent=send(m_iSock,(const char*)p_pData,p_iSize,0);
	if(iSent>=0){return iSent;};
	int iEC=WinSock::GetLastError();
	if(iEC==WSAEWOULDBLOCK)
	{
		return 0;
	};
	m_iLastError=iEC;
	return -1;
};

int TcpSocket::Recv(void* p_pData,int p_iSize)
{
	if(m_iSock==INVALID_SOCKET){return -1;};
	int iReceived=0;
	int iRet;
	do 
	{
		iRet=recv(m_iSock,((char*)p_pData)+iReceived,p_iSize-iReceived,0);
		if(iRet>0)
		{
			iReceived+=iRet;
		};
	} while ((iRet>0)&&(iReceived<p_iSize));
	if(iRet<0)
	{
		int iEC=WinSock::GetLastError();
		if(iEC!=WSAEWOULDBLOCK)
		{
			iReceived=-1;
			m_iLastError=iEC;
		};
	};
	return iReceived;
};

bool TcpSocket::CanSend()
{
	if(m_iSock==INVALID_SOCKET){return false;};
	fd_set xFS;
	xFS.fd_count=1;
	xFS.fd_array[0]=m_iSock;
	TIMEVAL xTV;
	xTV.tv_sec=0;
	xTV.tv_usec=10;
	return select(0,NULL,&xFS,NULL,&xTV)==1;
};

bool TcpSocket::CanRecv()
{
	if(m_iSock==INVALID_SOCKET){return false;};
	fd_set xFS;
	xFS.fd_count=1;
	xFS.fd_array[0]=m_iSock;
	TIMEVAL xTV;
	xTV.tv_sec=0;
	xTV.tv_usec=10;
	return select(0,&xFS,NULL,NULL,&xTV)==1;
};

int TcpSocket::PendingRecv()
{
	if(m_iSock==INVALID_SOCKET){return -1;};
	DWORD dwSize;
	int iRet=ioctlsocket(m_iSock,FIONREAD,&dwSize);
	if(iRet!=0)
	{
		m_iLastError=WinSock::GetLastError();
		return -1;
	};
	return (int)dwSize;
};

bool TcpSocket::HasHalfClose()
{
	if(m_iSock==INVALID_SOCKET){return false;};
	if(WSAWaitForMultipleEvents(1,&m_hCloseEvt,TRUE,0,FALSE)==WSA_WAIT_EVENT_0)
	{
		return true;
	};
	return false;
};

IPAddr TcpSocket::GetRemoteAddr() const
{
	if(m_iSock==INVALID_SOCKET){return IPAddr();};
	IPAddr xAddr;
	int iSize=sizeof(xAddr);
	getpeername(m_iSock,(sockaddr*)&xAddr,&iSize);
	return xAddr;
};

IPAddr TcpSocket::GetLocalAddr() const
{
	if(m_iSock==INVALID_SOCKET){return IPAddr();};
	IPAddr xAddr;
	int iSize=sizeof(xAddr);
	getsockname(m_iSock,(sockaddr*)&xAddr,&iSize);
	return xAddr;
};

bool TcpSocket::IsConnected() const
{
	return (m_iSock!=INVALID_SOCKET);
};

void TcpSocket::Swap(TcpSocket& po_xOther)
{
	int iSwap;
	iSwap=m_iSock;
	m_iSock=po_xOther.m_iSock;
	po_xOther.m_iSock=iSwap;

	iSwap=m_iLastError;
	m_iLastError=po_xOther.m_iLastError;
	po_xOther.m_iLastError=iSwap;

	void* hSwap;
	hSwap=m_hCloseEvt;
	m_hCloseEvt=po_xOther.m_hCloseEvt;
	po_xOther.m_hCloseEvt=hSwap;
};

bool TcpSocket::WaitForRecvData(int p_iTimeoutMS)
{
	if(m_iSock==INVALID_SOCKET){return false;};
	fd_set xFS;
	xFS.fd_count=1;
	xFS.fd_array[0]=m_iSock;
	TIMEVAL xTV;
	if(p_iTimeoutMS<=0)
	{
		xTV.tv_sec=0;
		xTV.tv_usec=10;
	}
	else
	{
		xTV.tv_sec=p_iTimeoutMS/1000;
		p_iTimeoutMS%=1000;
		xTV.tv_usec=p_iTimeoutMS*1000;
	};
	return select(0,&xFS,NULL,NULL,&xTV)==1;
};


TcpAcceptSocket::TcpAcceptSocket()
{
	m_iLastError=0;
	m_iSock=INVALID_SOCKET;
	m_hConReqEvt=WSA_INVALID_EVENT;
};

TcpAcceptSocket::~TcpAcceptSocket()
{
	Close();
};

bool TcpAcceptSocket::Open(const IPAddr& p_xAddr,int p_iMaxPending)
{
	Close();
	m_iSock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
	if(m_iSock==INVALID_SOCKET)
	{
		m_iLastError=WinSock::GetLastError();
		return false;
	};
	if(bind(m_iSock,(struct sockaddr*)&p_xAddr,sizeof(p_xAddr))!=0)
	{
		m_iLastError=WinSock::GetLastError();
		Close();
		return false;
	};
	if(p_iMaxPending==-1){p_iMaxPending=SOMAXCONN;};
	if(listen(m_iSock,p_iMaxPending)!=0)
	{
		m_iLastError=WinSock::GetLastError();
		Close();
		return false;
	};
	m_hConReqEvt=::WSACreateEvent();
	if(::WSAEventSelect(m_iSock,m_hConReqEvt,FD_ACCEPT)!=0)
	{
		m_iLastError=WinSock::GetLastError();
		Close();
		return false;
	};
	return true;
};

void TcpAcceptSocket::Close()
{
	if(m_iSock!=INVALID_SOCKET)
	{
		shutdown(m_iSock,SD_BOTH);
		closesocket(m_iSock);
		m_iSock=INVALID_SOCKET;
		if(m_hConReqEvt!=WSA_INVALID_EVENT)
		{
			::WSACloseEvent(m_hConReqEvt);
			m_hConReqEvt=WSA_INVALID_EVENT;
		};
	};
};

bool TcpAcceptSocket::ConnectionPending(int p_iTimeout)
{
	if(m_iSock==INVALID_SOCKET){return false;};
	if(::WSAWaitForMultipleEvents(1,&m_hConReqEvt,TRUE,p_iTimeout,FALSE)==WSA_WAIT_EVENT_0)
	{
		return true;
	}
	else
	{
		return false;
	};
};

bool TcpAcceptSocket::Accept(TcpSocket& po_xNewCon)
{
	if(m_iSock==INVALID_SOCKET){return false;};
	po_xNewCon.Close();
	sockaddr xA;
	int iAddrSize=sizeof(xA);
	po_xNewCon.m_iSock=::WSAAccept(m_iSock,&xA,&iAddrSize,NULL,0);
	if((po_xNewCon.m_iSock==INVALID_SOCKET)||(po_xNewCon.m_iSock<0))
	{
		m_iLastError=WinSock::GetLastError();
		po_xNewCon.m_iSock=INVALID_SOCKET;
		return false;
	};
	po_xNewCon.m_hCloseEvt=::WSACreateEvent();
	if(::WSAEventSelect(po_xNewCon.m_iSock,po_xNewCon.m_hCloseEvt,FD_CLOSE)!=0)
	{
		m_iLastError=WinSock::GetLastError();
		po_xNewCon.Close();
		return false;
	};
	return true;
};

UdpSocket::UdpSocket()
{
	m_iSock=INVALID_SOCKET;
	m_hReadData=WSA_INVALID_EVENT;
};

UdpSocket::~UdpSocket()
{
	Close();
};

void UdpSocket::Close()
{
	if(m_iSock!=INVALID_SOCKET)
	{
		shutdown(m_iSock,SD_BOTH);
		closesocket(m_iSock);
		m_iSock=INVALID_SOCKET;
	};
	if(m_hReadData!=WSA_INVALID_EVENT)
	{
		WSACloseEvent(m_hReadData);
		m_hReadData=WSA_INVALID_EVENT;
	};
};

bool UdpSocket::Open()
{
	Close();
	m_iSock=::WSASocket(AF_INET,SOCK_DGRAM,IPPROTO_UDP,NULL,0,WSA_FLAG_OVERLAPPED);
	if(m_iSock==INVALID_SOCKET){return false;};
	m_hReadData=::WSACreateEvent();
	::WSAEventSelect(m_iSock,m_hReadData,FD_READ);
	return true;
};

bool UdpSocket::Open(const IPAddr& p_xAddr)
{
	Close();
	m_iSock=::WSASocket(AF_INET,SOCK_DGRAM,IPPROTO_UDP,NULL,0,WSA_FLAG_OVERLAPPED);
	if(m_iSock==INVALID_SOCKET){return false;};
	if(bind(m_iSock,(const sockaddr*)&p_xAddr,sizeof(p_xAddr))!=0)
	{
		Close();
		return false;
	};
	m_hReadData=::WSACreateEvent();
	::WSAEventSelect(m_iSock,m_hReadData,FD_READ);
	return true;
};

int UdpSocket::SendTo(const IPAddr& p_xRemoteAddr,void* p_pData,int p_iSize)
{
	if(m_iSock==INVALID_SOCKET){return -1;};
	return sendto(m_iSock,(const char*)p_pData,p_iSize,0,
		(const sockaddr*)&p_xRemoteAddr,sizeof(p_xRemoteAddr));
};

int UdpSocket::Recv(void* p_pData,int p_iSize)
{
	if(m_iSock==INVALID_SOCKET){return -1;};
	return recv(m_iSock,(char*)p_pData,p_iSize,0);
};

int UdpSocket::RecvFrom(IPAddr& po_xRemoteAddr,void* p_pData,int p_iSize)
{
	if(m_iSock==INVALID_SOCKET){return -1;};
	int iAddrSize=sizeof(po_xRemoteAddr);
	return recvfrom(m_iSock,(char*)p_pData,p_iSize,0,
		(sockaddr*)&po_xRemoteAddr,&iAddrSize);
};

int UdpSocket::PendingRecv()
{
	if(m_iSock==INVALID_SOCKET){return -1;};
	DWORD iSize;
	if(ioctlsocket(m_iSock,FIONREAD,&iSize)!=0)
	{
		return -1;
	};
	return(int)iSize;
};

bool UdpSocket::IsOpen() const
{
	return m_iSock!=INVALID_SOCKET;
};




#pragma warning(pop)









